﻿#include "interface.h"
#include "context.h"
#include "font.h"
#include "ui/animation.h"
#include "audio/mixer.h"
#include <cmath>

using namespace anip;

constexpr vec2 GroupMargin = { 50, 70 };
constexpr float MainGroupPadding = 30;
constexpr float MainGroupHeight = 330;

constexpr color StoppedColor{ 0.9f, 0.9f, 0.9f, 1.0f };
constexpr color PlayColor{ 0.6f, 0.85f, 0.7f, 1.0f };
constexpr color PauseColor{ 0.82f, 0.82f, 0.35f, 1.0f };

constexpr color PlayedAreaColor{ 0.5f, 0.5f, 0.5f, 0.3f };
constexpr color VolumeAreaColor{ 0.6f, 0.6f, 0.6f, 0.3f };
constexpr color LoopAreaColor{ 0.65f, 0.25f, 0.5f, 0.4f };

void Interface::processKey(Key k, ButtonAction action)
{
    if (action != ButtonAction::Down) return;

    switch (k)
    {
    case anip::Key::Right:
        selectedGroup++;
        break;
    case anip::Key::Left:
        selectedGroup--;
        break;
    }

    ui::Animation::easeOutCubic(scroll, *this,
        vec2{ (_size.x - GroupMargin.x * 1.5f) * selectedGroup, 0.0f }, 0.4f);
}

void Interface::processParentResize()
{
    Window::processParentResize();
    scroll = vec2{ (_size.x - GroupMargin.x * 1.5f) * selectedGroup, 0 };
}

ShadowPanel& Interface::createGroupUI(SoundGroup& s, int i)
{
    vec2 size = _size - GroupMargin * 2.0f - vec2{ 0, MainGroupHeight + MainGroupPadding };
    auto panel = new ShadowPanel(this, 
        { GroupMargin.x + (size.x + GroupMargin.x * 0.5) * i, GroupMargin.y }, size);
    panel->setMargins({ 40, 90 }, { 40, 0 });
    panel->userCanScroll = false;

    panel->onParentResize = [=](ui::Control& self)
    {
        vec2 size = Context::size() - GroupMargin * 2.0f - vec2{ 0, MainGroupHeight + MainGroupPadding };
        self.setSize(size);
        self.setPosition({ GroupMargin.x + (size.x + GroupMargin.x * 0.5) * i, GroupMargin.y });
    };

    auto label = new ui::Label(panel, { -10, -70 }, { panel->size().x, 30 }, s.name);
    label->getFont().size = 42;

    auto content = new ui::Panel(panel);
    content->layoutOptions.dock = ui::Control::LayoutOptions::DockType::Fill;
    for (auto i : s.sounds)
    {
        createSoundUI(*i, *content);
    }
    return *panel;
}

void Interface::createSoundUI(Sound& s, Container& parent)
{
    struct DragData
    {
        MouseButton btn = MouseButton::None;
        double oldPos = 0, oldVol = 0;
    };

    auto panel = new ui::Draggable<ShadowPanel>(&parent, { 50, 50 }, { 300, 200 });
    panel->userCanScroll = false;
    panel->setMargins({ 10, 10 }, { 10, 10 });
    panel->backColor = { 0.9f, 0.9f, 0.9f, 1.0f };
    panel->layoutOptions.type = ui::Control::LayoutOptions::Flow;
    panel->roundRadius = 3;
    panel->shadowColor = { 0.5f, 0.5f, 0.5f, 0.0f };
    panel->shadowOffset = { 0, 0 };
    panel->userData = new DragData();

    auto text = ui::FlowHelper::label(*panel, s.name);
    text->getFont().size = 25;
    ui::FlowHelper::endRow(*panel);
    
    auto status = ui::FlowHelper::label(*panel, L"Stopped");
    status->getFont().size = 20;
    ui::FlowHelper::endRow(*panel);

    auto play = new ui::Button(panel, { -10, 100 }, { 100, 80 }, L"PLAY");
    play->normalColor = color{ 0.8f, 0.8f, 0.8f, 0.2f };
    play->onClick = [=, &s](ui::ButtonBase& self)
    {
        switch (s.status())
        {
        case PlayState::Paused:
            s.unpause();
            self.setText(L"PAUSE");
            ui::Animation::easeInOutCubic(panel->backColor, *panel,
                PlayColor, (float)max(0.3, s.fadeTime));
            break;
        case PlayState::Stopped:
            s.start();
            self.setText(L"PAUSE");
            ui::Animation::easeInOutCubic(panel->backColor, *panel,
                PlayColor, (float)max(0.3, s.fadeTime));
            break;
        case PlayState::Playing:
            s.pause();
            status->setText(L"Paused");
            self.setText(L"RESUME");
            ui::Animation::easeInOutCubic(panel->backColor, *panel,
                PauseColor, (float)max(0.3, s.fadeTime));
            break;
        }
    };

    s.onStop = [=](Sound& self)
    {
        play->setText(L"PLAY");
        status->setText(L"Stopped");
        ui::Animation::easeInOutCubic(panel->backColor, *panel,
            StoppedColor, 0.3f);
    };

    auto loop = new ui::Button(panel, { 90, 100 }, { 100, 80 }, L"LOOP");
    loop->normalColor = color{ 0.8f, 0.8f, 0.8f, 0.2f };
    loop->selectedColor = color{ 0.25f, 0.5f, 0.75f, 0.2f };
    loop->toggleOnClick = true;
    loop->select(s.looping);
    loop->onClick = [=, &s](ui::ButtonBase& self)
    {
        s.looping = self.isSelected();
    };

    auto stop = new ui::Button(panel, { 190, 100 }, { 100, 80 }, L"STOP");
    stop->normalColor = color{ 0.8f, 0.8f, 0.8f, 0.2f };
    stop->onClick = [=, &s](ui::ButtonBase& self)
    {
        s.stop();
    };

    auto change1 = new ui::Button(panel, { 240, 0 }, { 30, 30 }, L">");
    change1->onClick = [=](ui::ButtonBase&)
    {
        ui::Animation::easeOutCubic(panel->scroll, *panel,
            vec2{ 300, 0 }, 0.4f);
    };

    auto change2 = new ui::Button(panel, { 540, 0 }, { 30, 30 }, L"<");
    change2->onClick = [=](ui::ButtonBase&)
    {
        ui::Animation::easeOutCubic(panel->scroll, *panel,
            vec2{ 0, 0 }, 0.4f);
    };

    auto settings = new ui::Panel(panel, { 290, -10 }, { 300, 200 });
    settings->userCanScroll = false;

    auto fadeLabel = ui::FlowHelper::label(*settings, L"Fade: " + toString(s.fadeTime) + L"s");
    ui::FlowHelper::endRow(*settings);

    auto fade = ui::FlowHelper::slider(*settings, 0, 5, (float)s.fadeTime);
    fade->onValueChanged = [=, &s](ui::Slider& self)
    {
        s.fadeTime = self.value();
        fadeLabel->setText(L"Fading: " + toString(s.fadeTime) + L"s");
    };
    ui::FlowHelper::endRow(*settings);

    auto markIn = ui::FlowHelper::button(*settings, L"Mark loop-in [" + toString(s.loopIn) + L"s]");
    markIn->layoutOptions.stretchX = 1;
    markIn->onClick = [=, &s](ui::ButtonBase& self)
    {
        s.loopIn = s.currentPos();
        self.setText(L"Mark loop-in [" + toString(s.loopIn) + L"s]");
    };
    ui::FlowHelper::endRow(*settings);

    auto markOut = ui::FlowHelper::button(*settings, L"Mark loop-out [" + toString(s.loopOut) + L"s]");
    markOut->layoutOptions.stretchX = 1;
    markOut->onClick = [=, &s](ui::ButtonBase& self)
    {
        s.loopOut = s.currentPos();
        self.setText(L"Mark loop-out [" + toString(s.loopOut) + L"s]");
    };
    ui::FlowHelper::endRow(*settings);

    panel->onBeginDrag = [=, &s](ui::Draggable<ShadowPanel>& self, MouseButton btn, vec2 pos)
    {
        ((DragData*)self.userData)->btn = btn;
        ((DragData*)self.userData)->oldPos = s.currentPos();
        ((DragData*)self.userData)->oldVol = s.volume();
        return true;
    };

    panel->onDrag = [=, &s](ui::Draggable<ShadowPanel>& self, vec2 start, vec2 pos)
    {
        auto data = (DragData*)self.userData;
        if (data->btn == MouseButton::Middle)
        {
            double p = data->oldPos + s.runningTime() * ((pos.x - start.x) / self.size().x);
            if (p < 0) p = 0;
            if (p > s.runningTime()) p = s.runningTime();
            s.setPos(p);
            postSignal({ ui::Signal::Type::NeedsRedraw });
        }
        if (data->btn == MouseButton::Right)
        {
            double p = data->oldVol + (start.y - pos.y) / self.size().y;
            p = math::clamp(p, 0.0, 1.0);
            s.setVolume(p);
            postSignal({ ui::Signal::Type::NeedsRedraw });
        }
    };

    panel->onPaint = [=, &s](ui::Control& self)
    {
        float pos = (float)(s.currentPos() / s.runningTime());
        if (s.status() == PlayState::Playing)
        {
            postSignal({ ui::Signal::Type::Animation });
            int i = (int)(s.currentPos());
            int j = (int)(s.runningTime());
            status->setText(toString(i) + L"s / " + toString(j) + L"s ("
                + toString(round(pos * 100)) + L"%)");
        }

        vec2 abspos = self.toAbsolutePos() - self.margin() + panel->scroll();
        Draw::roundedRect(abspos, self.size() * vec2{ pos, 1 },
            panel->roundRadius, PlayedAreaColor);

        vec2 v = self.size() * vec2{ 1, s.volume() };
        Draw::roundedRect(abspos + vec2{ 0, self.size().y - v.y }, v,
            panel->roundRadius, VolumeAreaColor);

        if (s.looping)
        {
            Draw::roundedRect(
                abspos + self.size() * vec2{ s.loopIn / s.runningTime(), 0 }, 
                self.size() * vec2{ (s.loopOut - s.loopIn) / s.runningTime(), 1 },
                panel->roundRadius, LoopAreaColor);
        }
    };

    panel->onMouseEnter = [&](ui::Control& self)
    {
        auto& p = dynamic_cast<ShadowPanel&>(self);
        ui::Animation::linear(p.shadowColor, p, { 0.5f, 0.5f, 0.5f, 0.3f }, 0.2f);
    };

    panel->onMouseLeave = [&](ui::Control& self)
    {
        auto& p = dynamic_cast<ShadowPanel&>(self);
        ui::Animation::linear(p.shadowColor, p, { 0.6f, 0.6f, 0.6f, 0.0f }, 0.2f);
    };
}

Interface::Interface() :
    Window(nullptr, Context::size())
{
    markTopLevel();
    Font f = FontManager::get({ "std-latin", "std-chn" }, 20);
    setFont(f);
    setInheritFont(f);

    main = &createGroupUI(*MainGroup, 0);
    main->fixedPosition = true;
    main->setPosition(
        { GroupMargin.x, _size.y - GroupMargin.y - MainGroupHeight });
    main->setSize({ _size.x - 2 * GroupMargin.x, MainGroupHeight + 20 });

    main->onParentResize = [=](ui::Control& self)
    {
        main->setPosition(
            { GroupMargin.x, self.parent()->size().y - GroupMargin.y - MainGroupHeight });
        main->setSize({ self.parent()->size().x - 2 * GroupMargin.x, MainGroupHeight + 20 });
    };

    int i = 0;
    for (auto g : Groups)
        createGroupUI(*g, i++);

    auto save = new ui::Button(this, { 50, 20 }, { 200, 30 }, L"Save Configuration");
    save->fixedPosition = true;
    save->onClick = [=](ui::ButtonBase&)
    {
        for (auto i : MainGroup->sounds)
            i->writeConfig();
        for (auto i : Groups)
            for (auto j : i->sounds)
                j->writeConfig();
    };

    auto vol = new ui::Slider(this, { 300, 20 }, { _size.x - 350, 30 });
    vol->fixedPosition = true;
    vol->setValue(1);
    vol->onParentResize = [=](ui::Control& self)
    {
        self.setSize({ _size.x - 350, 30 });
    };
    vol->onValueChanged = [=](ui::Slider& s)
    {
        audio::Mixer::setGlobalVolume(s.value());
    };
}

ShadowPanel::ShadowPanel(Container* parent, vec2 pos, vec2 size):
    Panel(parent, pos, size)
{
    backColor = Colors::whiteSmoke;
    borderColor = { 0.9f, 0.9f, 0.9f, 1.0f };
    borderWidth = 2;
    roundRadius = 8;

    registerBindableRedraw(shadowColor);
    registerBindableRedraw(shadowSize);
    registerBindableRedraw(shadowOffset);
}

void ShadowPanel::paint()
{
    Draw::shadowRect(_absPos + shadowOffset(), _size, shadowColor, shadowSize);
    Panel::paint();
}
